#include "DataManager.h"
DataManager::DataManager(std::string path)
{
	this->freopen(path);
	hasFreopen = true;
	hasGetDemands = hasGetQosGraph = hasGetSiteLimits = hasOutputInit = false;
}


void DataManager::clearInput()
{
	hasFreopen = hasGetDemands = hasGetQosGraph = hasGetSiteLimits = hasOutputInit = false;
	this->demand.close();
	this->site.close();
	this->qos.close();
	this->config.close();

	this->qosLimit = 0;
	this->clientName.clear();
	this->siteName.clear();
	this->mpClientNameToID.clear();
	this->mpSiteNameToID.clear();
}

void DataManager::freopen(std::string path)
{
	this->clearInput();

	if (path[path.size() - 1] != '\\' && path[path.size() - 1] != '/')
	{
		path += '/';
	}
	demand.open(path + "demand.csv", std::ios::in);
	if (!demand.good())
	{
		std::cerr << "[InputManager Error]freopen(std::string path):Open demand.csv Error!\n";
		exit(0);
	}

	site.open(path + "site_bandwidth.csv", std::ios::in);
	if (!site.good())
	{
		std::cerr << "[InputManager Error]freopen(std::string path):Open site_bandwidth.csv Error!\n";
		exit(0);
	}
	qos.open(path + "qos.csv", std::ios::in);
	if (!qos.good())
	{
		std::cerr << "[InputManager Error]freopen(std::string path):Open qos.csv Error!\n";
		exit(0);
	}
	config.open(path + "config.ini", std::ios::in);
	if (!config.good())
	{
		std::cerr << "[InputManager Error]freopen(std::string path):Open config.ini Error!\n";
		exit(0);
	}
	hasFreopen = true;
	return;
}




void DataManager::getDemands(int& totleTime, int& clientNum, std::vector<int>& streamNum, std::vector<std::vector<std::vector<int>>>& bandwidthDemand)
{
	if (!hasFreopen)
	{
		std::cerr << "DataManager Error: You need to freopen before getDemands!\n";
		return;
	}
	std::string line;
	std::stringstream stream;
	std::string tmpString;


	std::getline(demand, line);
	stream << line;
	std::getline(stream, tmpString, ',');
	std::getline(stream, tmpString, ',');

	clientNum = 0;
	clientName.push_back("");
	while (std::getline(stream, tmpString, ','))
	{
		++clientNum;
		if (tmpString[tmpString.size() - 1] == '\n' || tmpString[tmpString.size() - 1] == '\r')
		{
			clientName.push_back(tmpString.substr(0, tmpString.size() - 1));
		}
		else
		{
			clientName.push_back(tmpString);
		}
	}

	totleTime = 0;
	bandwidthDemand.push_back(std::vector<std::vector<int>>{});
	timeStreamIDToName.push_back(std::vector<std::string>());
	streamNum.push_back(0);
	bool begin = true;
	std::string nowTime, tmpTime;
	int nowStreamNum = 0;
	std::vector<std::vector<int>> nowTimeInfo;
	std::vector<std::string> nowTimeStreamName;
	std::getline(demand, line);
	do
	{
		stream.clear();
		stream << line;
		std::getline(stream, tmpTime, ',');

		if (!begin && nowTime != tmpTime)
		{
			streamNum.push_back(nowStreamNum);
			bandwidthDemand.push_back(nowTimeInfo);
			this->timeStreamIDToName.push_back(nowTimeStreamName);
		}
		if (begin || nowTime != tmpTime)
		{
			++totleTime;
			nowTime = tmpTime;
			nowTimeInfo.clear();
			nowTimeInfo.push_back(std::vector<int>());
			nowTimeStreamName.clear();
			nowTimeStreamName.push_back(std::string());
			nowStreamNum = 0;
		}
		if (begin || nowTime == tmpTime)
		{
			++nowStreamNum;
			std::vector<int> nowTimeStreamInfo;
			nowTimeStreamInfo.push_back(0);

			std::getline(stream, tmpString, ',');
			nowTimeStreamName.push_back(tmpString);
			while (std::getline(stream, tmpString, ','))
			{
				nowTimeStreamInfo.push_back(std::stoi(tmpString));
			}
			nowTimeInfo.push_back(nowTimeStreamInfo);
			begin = false;
		}
	} while (std::getline(demand, line));
	streamNum.push_back(nowStreamNum);
	bandwidthDemand.push_back(nowTimeInfo);
	this->timeStreamIDToName.push_back(nowTimeStreamName);


	for (int clientID = 1; clientID <= clientNum; ++clientID)
	{
		mpClientNameToID[clientName[clientID]] = clientID;
	}
	this->clientNum = clientNum;
	this->timeNum = totleTime;
	hasGetDemands = true;
}

void DataManager::getSiteLimits(int& siteNum, std::vector<int>& bandwidthLimit)
{
	if (!hasFreopen)
	{
		std::cerr << "DataManager Error: You need to freopen before getSiteLimits!\n";
		return;
	}
	std::string line;
	std::stringstream stream;
	std::string tmpString;

	std::getline(site, line);

	siteNum = 0;
	siteName.push_back("");
	bandwidthLimit.push_back(0);
	while (std::getline(site, line))
	{
		++siteNum;
		stream.clear();
		stream << line;

		std::getline(stream, tmpString, ',');
		siteName.push_back(tmpString);
		std::getline(stream, tmpString, ',');
		bandwidthLimit.push_back(std::stoi(tmpString));
	}

	for (int siteID = 1; siteID <= siteNum; ++siteID)
	{
		mpSiteNameToID[siteName[siteID]] = siteID;
	}
	this->siteNum = siteNum;
	hasGetSiteLimits = true;
}

void DataManager::getQosGraph(int& baseCost, std::vector<std::pair<int, int>>& graph)
{
	if (!hasFreopen)
	{
		std::cerr << "DataManager Error: You need to freopen before getQosGraph!\n";
		return;
	}

	std::string line;
	std::stringstream stream;
	std::string tmpString;

	std::getline(config, line);
	std::getline(config, line);
	stream << line;
	std::getline(stream, tmpString, '=');
	std::getline(stream, tmpString, '=');
	qosLimit = std::stoi(tmpString);
	stream.clear();
	std::getline(config, line);
	stream << line;
	std::getline(stream, tmpString, '=');
	std::getline(stream, tmpString, '=');
	baseCost = std::stoi(tmpString);


	std::vector<int> colID, rowID;
	colID.push_back(0);
	rowID.push_back(0);

	graph.push_back(std::make_pair(0, 0));
	std::getline(qos, line);
	stream.clear();
	stream << line;
	std::getline(stream, tmpString, ',');
	while (std::getline(stream, tmpString, ','))
	{
		if (tmpString[tmpString.size() - 1] == '\n' || tmpString[tmpString.size() - 1] == '\r')
		{
			colID.push_back(mpClientNameToID[tmpString.substr(0, tmpString.size() - 1)]);
		}
		else
		{
			colID.push_back(mpClientNameToID[tmpString]);
		}
	}

	int row = 0, col = 0;
	while (std::getline(qos, line))
	{
		++row;
		col = 0;
		stream.clear();
		stream << line;
		std::getline(stream, tmpString, ',');
		rowID.push_back(mpSiteNameToID[tmpString]);
		while (std::getline(stream, tmpString, ','))
		{
			++col;
			if (qosLimit > std::stoi(tmpString))
				graph.push_back(std::make_pair(rowID[row], colID[col]));

		}
	}
	hasGetQosGraph = true;
}

void DataManager::outputInit()
{
	if (!hasFreopen)
	{
		std::cerr << "DataManager Error: You need to Freopen before output initialize.\n";
		return ;
	}
	if (!hasGetDemands)
	{
		std::cerr << "DataManager Error: You need to getDemands before output initialize.\n";
		return;
	}
	if (!hasGetQosGraph)
	{
		std::cerr << "DataManager Error: You need to getQosGraph before output initialize.\n";
		return;
	}
	if (!hasGetSiteLimits)
	{
		std::cerr << "DataManager Error: You need to getSiteLimits before output initialize.\n";
		return;
	}

	nowTime = nowSite = nowClient = 0;
	log.clear();
	log.resize(timeNum + 1, std::vector<std::vector<std::vector<int>>>(clientNum + 1, std::vector<std::vector<int>>(siteNum + 1, std::vector<int>())));

	this->hasOutputInit = true;
}


void DataManager::setTime(int timeID)
{
	if (!hasOutputInit)
	{
		std::cerr << "You need to use outputInit before setTime.\n";
		return;
	}
	if (timeID < 1 || timeID > timeNum)
	{
		std::cerr << "[OutputManager Error]setTime(int timeID):timeID is incorrect.(" << timeID << ")";
		return;
	}
	this->nowTime = timeID;
}

void DataManager::setClient(int clientID)
{
	if (!hasOutputInit)
	{
		std::cerr << "You need to use outputInit before setClient.\n";
		return;
	}
	if (clientID < 1 || clientID > clientNum)
	{
		std::cerr << "[OutputManager Error]setClient(int clientID):clientID is incorrect.(" << clientID << ")";
		return;
	}
	this->nowClient = clientID;
}

void DataManager::setSite(int siteID)
{
	this->nowSite = siteID;
}

void DataManager::writeLog(int timeID, int clientID, int siteID, int streamID)
{
	if (!hasOutputInit)
	{
		std::cerr << "You need to use outputInit before writeLog.\n";
		return;
	}
	log[timeID][clientID][siteID].push_back(streamID);
}

void DataManager::writeLog(int streamID)
{
	if (!hasOutputInit)
	{
		std::cerr << "You need to use outputInit before writeLog.\n";
		return;
	}
	log[nowTime][nowClient][nowSite].push_back(streamID);
}

void DataManager::writeFile(std::string filePath)
{
	if (!hasOutputInit)
	{
		std::cerr << "You need to use outputInit before writeFile.\n";
		return;
	}
	std::ofstream file(filePath);
	for (int timeID = 1; timeID <= timeNum; ++timeID)
	{
		for (int clientID = 1; clientID <= clientNum; ++clientID)
		{
			file << clientName[clientID] << ":";
			bool tag = true;
			for (int siteID = 1; siteID <= siteNum; ++siteID)
			{
				if (!log[timeID][clientID][siteID].empty())
				{
					if (!tag) file << ",";
					else tag = false;
					file << "<" << siteName[siteID];
					for (auto& streamID : log[timeID][clientID][siteID])
					{
						file << "," << timeStreamIDToName[timeID][streamID];
					}
					file << ">";
				}
			}
			file << "\n";
		}
	}
}

void DataManager::clearOutputInfor()
{
	if (!hasOutputInit)
	{
		std::cerr << "You need to use outputInit before clearOutputInfor.\n";
		return;
	}
	nowTime = nowSite = nowClient = 0;
	log.clear();
	log.resize(timeNum + 1, std::vector<std::vector<std::vector<int>>>(clientNum + 1, std::vector<std::vector<int>>(siteNum + 1, std::vector<int>())));
}


void DataManager::debugDataManager()
{
	int totleTime, clientNum, siteNum, baseCost;
	std::vector<int> streamNum;
	std::vector<std::vector<std::vector<int>>> bandwidthDemand;
	std::vector<int> bandwidthLimit;
	std::vector<std::pair<int, int>> graph;

	getDemands(totleTime, clientNum, streamNum, bandwidthDemand);
	getSiteLimits(siteNum, bandwidthLimit);
	getQosGraph(baseCost, graph);

	std::cout << "TotleTime: " << totleTime << "\n";
	std::cout << "ClientNum: " << clientNum << "\n";
	std::cout << "SiteNum: " << siteNum << "\n";
	std::cout << "baseCost: " << baseCost << "\n";
	for (int timeID = 1; timeID <= totleTime; ++timeID)
	{
		for (int streamID = 1; streamID <= streamNum[timeID]; ++streamID)
		{
			for (int clientID = 1; clientID <= clientNum; ++clientID)
			{
				std::cout << timeID << ", " << clientName[clientID] << ", " << timeStreamIDToName[timeID][streamID] << "," << bandwidthDemand[timeID][streamID][clientID] << "\n";
			}
		}
	}

	for (int siteID = 1; siteID <= siteNum; ++siteID)
	{
		std::cout << siteName[siteID] << ", " << bandwidthLimit[siteID];
	}

	for (auto& pair : graph)
	{
		std::cout << "<" << pair.first << ", " << pair.second << ">\n";
	}

	this->outputInit();

	this->setTime(10);
	this->setClient(1);
	this->setSite(2);
	this->writeLog(5);
	this->writeLog(10);
	this->setSite(3);
	this->writeLog(2);
	this->writeLog(4);

	this->writeFile("../../output/test.txt");
}

